/* A simple server in the internet domain using TCP
   The port number is passed as an argument 
   This version runs forever, forking off a separate 
   process for each connection
*/

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h> 
#include <sys/socket.h>
#include <netinet/in.h>
#include <iostream>
#include <string.h>
#include <map>
#include "Frame.h"
#include <locale>
#include <signal.h>
#include<sched.h>
 

 
typedef struct client_instance
{
        client_instance(){}
        client_instance(const client_instance &ci)
        {
                pid=0;
                socket=0;
                tiempo=0;
                strcpy(nombre,"none");
        }
        pid_t pid;
        int socket;
        int tiempo;
        char nombre[64]; 
};

typedef struct params
{
    int socket;
    long hash;
    bool reconexion;
};


std::map<long,client_instance>clients;
int Message_queue_setup(const int & queue_key)
{
    int id_message_queue;
    key_t key; 
    key = ftok ("/bin/ls", queue_key);
    if (key == (key_t)-1)
    {
        std::cout << "Error al obtener clave para cola mensajes" << std::endl;
        return(EXIT_FAILURE);
    } 
    
        id_message_queue = msgget (key, 0600 | IPC_CREAT);
    if (id_message_queue == -1)
    {
        std::cout << "Error al obtener identificador para cola mensajes" << std::endl;
        return(EXIT_FAILURE);
    }
        
    return (id_message_queue);
}

int cola_1(void*)
{
    std::cout<<"estoy corriendo\n";
    std::map<long,client_instance>::iterator it;
    int key,key_2;
    message_queue bookmark;
    message_queue outbound_mail;
    key=Message_queue_setup(I_QUEUE_KEY);
    key_2=Message_queue_setup(C_PACKER_QUEUE_KEY);
    std::cout<<"key: " << int(key_2)<<"\n";
    std::locale loc;
    const std::collate<char>& coll = std::use_facet<std::collate<char> >(loc);
    std::string seed;
    long hash;
    struct client_instance cl_i;
    char port[6];
    
    
    while(1)
    {
        sleep(1);
        msgrcv(key, (struct msgbuf *)&outbound_mail,
        sizeof(outbound_mail.package)+sizeof(outbound_mail.cli_addr),0,0);
        
        print_output(outbound_mail.package);
        seed=inet_ntoa(outbound_mail.package.id_client.sin_addr);
        sprintf(port,"%d",ntohs(outbound_mail.package.id_client.sin_port)); 
        seed+=port;
        std::cout<<"esta es la semilla: "<<seed<<std::endl;
        hash=coll.hash(seed.data(),seed.data()+seed.length());
        std::cout<<"el hash en cola: "<<int(hash)<<std::endl;
        if(outbound_mail.package.header.type_message==ADOPTION_PAPERS)
        {
            
            for(it=clients.begin();it!=clients.end();it++)
                std::cout<<(*it).first<<"=>"<<(*it).second.nombre<<std::endl;
            
            it=clients.find(hash);
            if(it==clients.end())
                std::cout<<"no lo encuentro\n";
            
            std::cout<<"entre al if\n";
            std::cout<<int(hash)<<std::endl;
            std::cout<<(*it).second.nombre<<std::endl;
            close((*it).second.socket);
            //kill((*it).second.pid, SIGTERM);
            //sprintf(cmd,"kill %d",(*it).second.pid);
            //system((char *)cmd);
            std::cout<<"sali del proceso\n";
            bzero((char *) &bookmark, sizeof(bookmark));   
            bookmark.package=outbound_mail.package;
            bookmark.cli_addr=outbound_mail.package.ip_server_guardian;
            bookmark.package.bookmark.tiempo=(*it).second.tiempo;
            strcpy(bookmark.package.bookmark.nombre,(*it).second.nombre);
            bookmark.package.header.id_server_destination=outbound_mail.package.id_server_guardian;
            bookmark.package.header.id_server_source=outbound_mail.package.header.id_server_destination; 
            bookmark.package.header.offset+=sizeof(bookmark.package.bookmark);          
            bookmark.package.header.type_message=BOOKMARK;
            bookmark.id_message=HIGHT_PRIORITY;
            bookmark.package.id_client=outbound_mail.package.id_client;
            msgsnd(key_2, (struct msgbuf *)&bookmark,
            sizeof(bookmark.package)+sizeof(bookmark.cli_addr),IPC_NOWAIT);
            it=clients.find(hash);
            //clients.erase(it);
        }
        
        else if(outbound_mail.package.header.type_message==BOOKMARK)
        {
            std::cout<<"he procesado el bookmar\n";
            it=clients.begin();
            clients.insert(std::pair<long,client_instance>(hash,cl_i));
            it=clients.find(hash);
            strcpy((*it).second.nombre,outbound_mail.package.bookmark.nombre);
            (*it).second.tiempo=outbound_mail.package.bookmark.tiempo;
            
            char msg[]="ready";
            int sockfd, n;
            struct sockaddr_in serv_addr;
            unsigned int serv_leng=sizeof(serv_addr);
            bzero((char *) &serv_addr, sizeof(serv_addr));
            sockfd = socket(AF_INET, SOCK_DGRAM, 0);
            serv_addr.sin_family = AF_INET;
            serv_addr.sin_addr=outbound_mail.package.id_client.sin_addr;
            serv_addr.sin_port = htons(QUIMERA_PORT);
      
            n = sendto(sockfd, &msg,sizeof(msg), 0, (struct sockaddr *)&serv_addr, serv_leng);
            std::cout<<"Bytes enviados: "<<n<<std::endl;
            close(sockfd);
  
        }
        
   }
    return(0);
 } 
int dostuff(void *); /* function prototype */
void error(const char *msg)
{
    std::cout<<"error"<<std::endl;
    perror(msg);
    //exit(1);
}

int main(int argc, char *argv[])
{
     std::map<long,client_instance>::iterator it; 
     params args;
     void *stack=malloc(65536);
     client_instance cl_i;
     int sockfd, newsockfd, portno, optval;
     pid_t pid;
     socklen_t clilen;
     struct sockaddr_in serv_addr, cli_addr;
     std::locale loc;
     const std::collate<char>& coll = std::use_facet<std::collate<char> >(loc);
     std::string seed;
     long hash;
     char port[6];
     socklen_t optlen = sizeof(optval);
     bool reconexion;
     if (argc < 2) {
         fprintf(stderr,"ERROR, no port provided\n");
         exit(1);
     }
     sockfd = socket(AF_INET, SOCK_STREAM, 0);
     
     optval = 1;
     optlen = sizeof(optval);
     if(setsockopt(sockfd, SOL_SOCKET, SO_KEEPALIVE, &optval, optlen) < 0) 
         perror("setsockopt()");
     
    
     if(sockfd < 0) 
        error("ERROR opening socket");
    
     bzero((char *) &serv_addr, sizeof(serv_addr));
     portno = atoi(argv[1]);
     serv_addr.sin_family = AF_INET;
     serv_addr.sin_addr.s_addr = INADDR_ANY;
     serv_addr.sin_port = htons(portno);
   
     if (bind(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0) 
         error("ERROR on binding");
     
     listen(sockfd,5);
     clilen = sizeof(cli_addr);
     
     pid=clone(cola_1,stack+65536,0,NULL);
    
     if (pid > 0)
     {
       
         while (true) 
         {
             newsockfd = accept(sockfd,(struct sockaddr *) &cli_addr, &clilen);
         
             if (newsockfd < 0)     
                 error("ERROR on accept");
         
                   
             seed=inet_ntoa(cli_addr.sin_addr);
             sprintf(port,"%d",ntohs(cli_addr.sin_port)); 
             seed+=port;
             std::cout<<"esta es la semilla: "<<seed<<std::endl;
             hash=coll.hash(seed.data(),seed.data()+seed.length());
             std::cout<<"Este es el hash en el registro: "<<int(hash)<<std::endl;
             it=clients.find(hash);
                 
             if(it==clients.end())
             {
                 reconexion=false;
                 it=clients.begin();
                 clients.insert(std::pair<long,client_instance>(hash,cl_i)); 
             }
             else
             {
                 reconexion=true;
                 
             }
             args.hash=hash;
             args.reconexion=reconexion;
             args.socket=newsockfd;
             
             void* arg =(void*)&args;
             pid=clone(dostuff, stack+65536, 0, arg);
             
             it=clients.find(hash);
             (*it).second.pid=pid;
             (*it).second.socket=newsockfd;
             std::cout<<"Este es el hash al llamar al cliente: "<<int(hash)<<std::endl;
            
            if (pid < 0)
               error("ERROR on thread");
            
         }  /* end of while */
     }
     close(sockfd);
     return 0; /* we never get here */
}

/******** DOSTUFF() *********************
 There is a separate instance of this function 
 for each connection.  It handles all communication
 once a connnection has been established.
 *****************************************/
int dostuff (void *args)
{
    std::map<long,client_instance>::iterator it;
    params *arg;
    arg=(params*)malloc(sizeof(params));
    arg=(params*)args;
    char buffer[255];
    char nombre[64];
    char id[]="\nSoy el servidor A y has estado con migo:";
    char cont[10];
    int n, r=0;  
    bzero((char *) &nombre, sizeof(nombre));
    it=clients.find(arg->hash);
    if(it==clients.end())
                std::cout<<"no lo encuentro\n";
    else
         std::cout<<"SIIIIIIIIIIII lo encuentro\n";
    
    std::cout<<"este es el valor del hash en el cliente: "<<int(arg->hash)<<std::endl;
    if(arg->reconexion)
    {
         
         strcpy(nombre,(*it).second.nombre);
         r=(*it).second.tiempo;
        
    }
    else
    {
        
        n = read(arg->socket,nombre,sizeof(nombre));
        strcpy((*it).second.nombre,nombre); 
    }
    std::cout << (*it).first << "=>" << (*it).second.nombre << std::endl;
    std::cout<<"Este es el hash en el cliente: "<<int(arg->hash)<<std::endl;
    
    while(true)
    {
       sleep (1);
       bzero((char *) &buffer, sizeof(buffer));
       bzero((char *) &cont, sizeof(cont));
       r++;
       (*it).second.tiempo=r;
       sprintf(cont,"%d",r);
       strcat(buffer,"\nHola: ");
       strcat(buffer,nombre);
       strcat(buffer,id);
       strcat(buffer,cont);
       strcat(buffer," segundos\n");
       n = write(arg->socket,buffer,sizeof(buffer));
       if (n < 0) 
           std::cout<<"error"<<std::endl;
   }
}